#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Gitee Webhook 服务器
接收Gitee的Tag推送事件，自动触发部署

使用方法：
1. 安装依赖：pip3 install flask
2. 运行服务：python3 gitee-webhook-server.py
3. 在Gitee仓库设置中配置Webhook
"""

import os
import json
import hmac
import hashlib
import subprocess
from flask import Flask, request, jsonify

app = Flask(__name__)

# ==================== 配置区域 ====================
# Webhook密钥（在Gitee仓库设置中配置）
WEBHOOK_SECRET = os.environ.get('WEBHOOK_SECRET', 'your-webhook-secret')

# 部署目录
DEPLOY_DIR = os.environ.get('DEPLOY_DIR', '/opt/aps/deployment')

# 镜像仓库配置
IMAGE_REGISTRY = os.environ.get('IMAGE_REGISTRY', 'registry.cn-hangzhou.aliyuncs.com')
IMAGE_NAMESPACE = os.environ.get('IMAGE_NAMESPACE', 'your-namespace')

# Tag规则：只有匹配的tag才会触发部署
# 例如：v1.0.0, v2.1.3, release-1.0.0
TAG_PATTERN = r'^(v|release-)[\d\.]+$'

# 日志文件
LOG_FILE = '/var/log/aps-deploy.log'
# ==================================================


def log(message):
    """记录日志"""
    import datetime
    timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    log_msg = f"[{timestamp}] {message}"
    print(log_msg)
    
    try:
        with open(LOG_FILE, 'a') as f:
            f.write(log_msg + '\n')
    except Exception as e:
        print(f"写入日志失败: {e}")


def verify_signature(data, signature):
    """验证Gitee Webhook签名"""
    if not WEBHOOK_SECRET or WEBHOOK_SECRET == 'your-webhook-secret':
        log("警告: 未设置WEBHOOK_SECRET，跳过签名验证")
        return True
    
    mac = hmac.new(
        WEBHOOK_SECRET.encode('utf-8'),
        msg=data,
        digestmod=hashlib.sha256
    )
    expected_signature = mac.hexdigest()
    
    return hmac.compare_digest(expected_signature, signature)


def is_valid_tag(tag_name):
    """检查tag是否符合部署规则"""
    import re
    return re.match(TAG_PATTERN, tag_name) is not None


def run_command(command, cwd=None):
    """执行shell命令"""
    try:
        log(f"执行命令: {command}")
        result = subprocess.run(
            command,
            shell=True,
            cwd=cwd,
            capture_output=True,
            text=True,
            timeout=600  # 10分钟超时
        )
        
        if result.returncode == 0:
            log(f"命令执行成功: {result.stdout}")
            return True, result.stdout
        else:
            log(f"命令执行失败: {result.stderr}")
            return False, result.stderr
    except subprocess.TimeoutExpired:
        log("命令执行超时")
        return False, "命令执行超时"
    except Exception as e:
        log(f"命令执行异常: {str(e)}")
        return False, str(e)


def deploy(tag_name, repo_url):
    """执行部署"""
    log(f"开始部署 tag: {tag_name}")
    
    # 构建镜像标签
    image_tag = tag_name.replace('v', '').replace('release-', '')
    backend_image = f"{IMAGE_REGISTRY}/{IMAGE_NAMESPACE}/aps-backend:{image_tag}"
    frontend_image = f"{IMAGE_REGISTRY}/{IMAGE_NAMESPACE}/aps-frontend:{image_tag}"
    
    log(f"后端镜像: {backend_image}")
    log(f"前端镜像: {frontend_image}")
    
    # 步骤1: 拉取最新镜像
    log("步骤1: 拉取Docker镜像...")
    success, output = run_command(f"docker pull {backend_image}", cwd=DEPLOY_DIR)
    if not success:
        return False, f"拉取后端镜像失败: {output}"
    
    success, output = run_command(f"docker pull {frontend_image}", cwd=DEPLOY_DIR)
    if not success:
        return False, f"拉取前端镜像失败: {output}"
    
    # 步骤2: 更新.env文件
    log("步骤2: 更新环境变量...")
    env_content = f"""
# 自动部署更新 - {tag_name}
BACKEND_IMAGE={backend_image}
FRONTEND_IMAGE={frontend_image}
"""
    
    try:
        env_file = os.path.join(DEPLOY_DIR, '.env')
        with open(env_file, 'r') as f:
            existing_content = f.read()
        
        # 更新镜像配置
        with open(env_file, 'a') as f:
            f.write(env_content)
        
        log("环境变量已更新")
    except Exception as e:
        log(f"更新环境变量失败: {e}")
        return False, f"更新环境变量失败: {e}"
    
    # 步骤3: 重启服务
    log("步骤3: 重启服务...")
    success, output = run_command(
        "docker compose -f docker-compose.prod.yml up -d --force-recreate",
        cwd=DEPLOY_DIR
    )
    if not success:
        return False, f"重启服务失败: {output}"
    
    # 步骤4: 等待服务启动
    log("步骤4: 等待服务启动...")
    import time
    time.sleep(10)
    
    # 步骤5: 健康检查
    log("步骤5: 健康检查...")
    success, output = run_command(
        "docker exec aps-backend curl -f http://localhost:8080/actuator/health",
        cwd=DEPLOY_DIR
    )
    if not success:
        log("警告: 健康检查失败，但部署已完成")
    
    log(f"部署完成: {tag_name}")
    return True, "部署成功"


@app.route('/webhook', methods=['POST'])
def webhook():
    """处理Gitee Webhook"""
    try:
        # 获取请求数据
        data = request.get_data()
        signature = request.headers.get('X-Gitee-Token', '')
        
        # 验证签名
        if not verify_signature(data, signature):
            log("签名验证失败")
            return jsonify({'error': '签名验证失败'}), 403
        
        # 解析JSON
        payload = json.loads(data)
        
        # 获取事件类型
        event = request.headers.get('X-Gitee-Event', '')
        log(f"收到Gitee事件: {event}")
        
        # 只处理Tag Push事件
        if event != 'Tag Push Hook':
            log(f"忽略事件: {event}")
            return jsonify({'message': '忽略非Tag事件'}), 200
        
        # 获取tag信息
        ref = payload.get('ref', '')
        tag_name = ref.replace('refs/tags/', '')
        repo_url = payload.get('repository', {}).get('git_url', '')
        
        log(f"Tag: {tag_name}, Repo: {repo_url}")
        
        # 检查tag是否符合规则
        if not is_valid_tag(tag_name):
            log(f"Tag不符合部署规则: {tag_name}")
            return jsonify({'message': f'Tag不符合规则: {tag_name}'}), 200
        
        # 执行部署
        success, message = deploy(tag_name, repo_url)
        
        if success:
            return jsonify({
                'status': 'success',
                'message': message,
                'tag': tag_name
            }), 200
        else:
            return jsonify({
                'status': 'failed',
                'message': message,
                'tag': tag_name
            }), 500
            
    except Exception as e:
        log(f"处理Webhook异常: {str(e)}")
        return jsonify({'error': str(e)}), 500


@app.route('/health', methods=['GET'])
def health():
    """健康检查"""
    return jsonify({'status': 'ok'}), 200


@app.route('/', methods=['GET'])
def index():
    """首页"""
    return """
    <h1>Gitee Webhook 服务器</h1>
    <p>状态: 运行中</p>
    <p>Webhook URL: http://your-server:5000/webhook</p>
    <p>健康检查: <a href="/health">/health</a></p>
    """


if __name__ == '__main__':
    log("启动Gitee Webhook服务器...")
    log(f"部署目录: {DEPLOY_DIR}")
    log(f"镜像仓库: {IMAGE_REGISTRY}/{IMAGE_NAMESPACE}")
    log(f"Tag规则: {TAG_PATTERN}")
    
    # 生产环境建议使用gunicorn运行
    # gunicorn -w 4 -b 0.0.0.0:5000 gitee-webhook-server:app
    app.run(host='0.0.0.0', port=5000, debug=False)

